﻿using System;
using System.Collections.Generic;
using System.Linq;
using UltimatePlugin.Db.SpellManagement;
using UltimatePlugin.Db.SpellManagement.Class;
using UltimatePlugin.Db.TargetManagement;
using UltimatePlugin.Technicals;
using Zeta;
using Zeta.Common;
using Zeta.CommonBot;
using Zeta.CommonBot.Settings;
using Zeta.Internals.Actors;

namespace UltimatePlugin.Db.Cache
{
    internal static class HeroCache
    {
        private static DateTime _ExtendedKillRadiusStartDate;
        private static ushort _ExtendedKillRadiusDelay;
        private static float _ExtendedKillRadiusValue;
        private static ISpellSelector _SpellSelector;

        static HeroCache()
        {
            NextPosition = Vector3.Zero;
        }

        public static Vector3 Position
        {
            get;
            private set;
        }

        public static ActorClass ActorClass
        {
            get;
            private set;
        }

        public static double HitpointsCurrent
        {
            get;
            private set;
        }

        public static double HitpointsCurrentPct
        {
            get;
            private set;
        }

        public static bool HasEmpowered
        {
            get;
            private set;
        }

        public static IDictionary<EnergyType, float> Energy
        {
            get;
            private set;
        }

        public static IDictionary<SNOPower, int> Buffs
        {
            get;
            private set;
        }

        public static IList<SNOPower> HotBarPowers
        {
            get;
            private set;
        }

        public static bool TownRunNeeded
        {
            get;
            private set;
        }

        public static bool IsInCombat
        {
            get;
            private set;
        }

        public static AnimationState AnimationState
        { 
            get; 
            private set; 
        }

        public static SNOAnim AnimationSNO
        { 
            get; 
            private set; 
        }

        public static float LootRadius
        {
            get;
            private set;
        }

        public static float KillRadius
        {
            get;
            private set;
        }

        public static ISpellSelector SpellSelector
        {
            get
            {
                return _SpellSelector;
            }
        }

        public static Vector3 NextPosition
        {
            get;
            private set;
        }

        public static float PositionTolerance
        {
            get;
            private set;
        }

        public static bool IsInTown
        { 
            get; 
            set; 
        }

        public static void Refresh()
        {
            try
            {
                if (!ZetaDia.IsInGame || ZetaDia.IsLoadingWorld)
                {
                    return;
                }
                using (ZetaDia.Memory.AcquireFrame())
                {
                    ZetaDia.Actors.Update();
                    Position = ZetaDia.Me.Position;
                    ActorClass = ZetaDia.Me.ActorClass;
                    HitpointsCurrent = ZetaDia.Me.HitpointsCurrent;
                    HitpointsCurrentPct = ZetaDia.Me.HitpointsCurrentPct;
                    HeroCache.AnimationState = ZetaDia.Me.CommonData.AnimationInfo.State;
                    HeroCache.AnimationSNO = ZetaDia.Me.CommonData.AnimationInfo.Current;
                    IsInTown = ZetaDia.Me.IsInTown;
                    if (IsInTown)
                    {
                        if (ZetaDia.Me.Inventory.NumFreeBackpackSlots * 100 / ZetaDia.Me.NumBackpackSlots > 80)
                        {
                            TownRunNeeded = true;
                        }
                    }

                    if (Position.Distance(NextPosition) < PositionTolerance)
                    {
                        NextPosition = Vector3.Zero;
                    }

                    GetBuffs();
                    GetHotBarPower();
                    GetEnergy();
                    GetSpellSelector();
                    IsInCombat = (TargetManager.CurrentTarget == null);

                    if (DateTime.UtcNow <= _ExtendedKillRadiusStartDate.AddSeconds(_ExtendedKillRadiusDelay))
                    {
                        KillRadius = _ExtendedKillRadiusValue;
                    }
                    else
                    {
                        if (ProfileManager.CurrentProfile.KillMonsters)
                        {
                            KillRadius = CharacterSettings.Instance.KillRadius;
                        }
                        else
                        {
                            KillRadius = 3; // 
                        }
                    }

                    if (ProfileManager.CurrentProfile.PickupLoot)
                    {
                        LootRadius = CharacterSettings.Instance.LootRadius;
                    }
                    else
                    {
                        LootRadius = -1; // 
                    }
                }
            }
            catch
            { }
        }

        private static void GetSpellSelector()
        {
            if (SpellSelector != null)
                return;
            switch (ActorClass)
            {
                case Zeta.Internals.Actors.ActorClass.Monk:
                    _SpellSelector = new MonkSpellSelector();
                    break;
                case Zeta.Internals.Actors.ActorClass.Barbarian:
                    _SpellSelector = new BarbarianSpellSelector();
                    break;
                case Zeta.Internals.Actors.ActorClass.DemonHunter:
                case Zeta.Internals.Actors.ActorClass.WitchDoctor:
                case Zeta.Internals.Actors.ActorClass.Wizard:
                    break;
            }
        }

        public static void DefinePositionTolerance(float tolerance)
        {
            PositionTolerance = tolerance;
        }

        public static void DefineNextPosition(Vector3 to)
        {
            if (NextPosition == Vector3.Zero)
            {
                NextPosition = to;
            }
        }

        public static void ForceTownRun()
        {
            TownRunNeeded = true;
        }

        public static void ExtendKillRadius(ushort delay, float extendedKillRadius)
        {
            if (extendedKillRadius == 0 || extendedKillRadius > CharacterSettings.Instance.KillRadius)
            {
                if (extendedKillRadius == 0)
                {
                    _ExtendedKillRadiusValue = CharacterSettings.Instance.KillRadius * 2;
                }
                else
                {
                    _ExtendedKillRadiusValue = extendedKillRadius;
                }
                _ExtendedKillRadiusDelay = delay;
                _ExtendedKillRadiusStartDate = DateTime.UtcNow;
            }
        }
 
        private static void GetEnergy()
        {
            Dictionary<EnergyType, float> energy = new Dictionary<EnergyType, float>();
            switch (ActorClass)
            {
                case Zeta.Internals.Actors.ActorClass.Barbarian:
                    SetEnergy(energy, EnergyType.Fury, ZetaDia.Me.CurrentPrimaryResource);
                    break;
                case Zeta.Internals.Actors.ActorClass.Monk:
                    SetEnergy(energy, EnergyType.Spirit, ZetaDia.Me.CurrentPrimaryResource);
                    break;
                case Zeta.Internals.Actors.ActorClass.DemonHunter:
                    SetEnergy(energy, EnergyType.Hatred, ZetaDia.Me.CurrentPrimaryResource);
                    SetEnergy(energy, EnergyType.Discipline, ZetaDia.Me.CurrentSecondaryResource);
                    break;
                case Zeta.Internals.Actors.ActorClass.Wizard:
                    SetEnergy(energy, EnergyType.Arcane, ZetaDia.Me.CurrentPrimaryResource);
                    break;
                case Zeta.Internals.Actors.ActorClass.WitchDoctor:
                    SetEnergy(energy, EnergyType.Mana, ZetaDia.Me.CurrentPrimaryResource);
                    break;
            }
            Energy = new ReadOnlyDictionary<EnergyType, float>(energy);
        }

        private static void GetHotBarPower()
        {
            IList<SNOPower> hotBarPower = new List<SNOPower>();
            hotBarPower.Add(ZetaDia.Me.GetHotbarPowerId(HotbarSlot.HotbarMouseLeft));
            hotBarPower.Add(ZetaDia.Me.GetHotbarPowerId(HotbarSlot.HotbarMouseRight));
            hotBarPower.Add(ZetaDia.Me.GetHotbarPowerId(HotbarSlot.HotbarSlot1));
            hotBarPower.Add(ZetaDia.Me.GetHotbarPowerId(HotbarSlot.HotbarSlot2));
            hotBarPower.Add(ZetaDia.Me.GetHotbarPowerId(HotbarSlot.HotbarSlot3));
            hotBarPower.Add(ZetaDia.Me.GetHotbarPowerId(HotbarSlot.HotbarSlot4));
            HotBarPowers = new List<SNOPower>(hotBarPower.Where(s => s != SNOPower.None));
        }

        private static void SetEnergy(Dictionary<EnergyType, float> dic, EnergyType energy, float value)
        {
            if (dic.ContainsKey(energy))
            {
                dic[energy] = value;
            }
            else
            {
                dic.Add(energy, value);
            }
        }

        private static void GetBuffs()
        {
            IDictionary<SNOPower, int> buffs = new Dictionary<SNOPower, int>();
            foreach (Buff buff in ZetaDia.Me.GetAllBuffs())
            {
                if (!buffs.ContainsKey((SNOPower)buff.SNOId))
                {
                    buffs.Add((SNOPower)buff.SNOId, buff.StackCount);
                }
            }
            Buffs = new ReadOnlyDictionary<SNOPower, int> (buffs);

        }

    }
}
